package com.kiki.song;

import java.util.*;
import java.util.concurrent.*;

/**
 * 音乐实时推荐系统核心实现(原生版本)
 * 结合用户协同过滤（User-CF）和物品特征推荐（Item-Based）的混合策略
 */
public class MusicRealTimeRecommender {


    // ================== 数据结构定义 ==================
    //=======(用户行为、用户记录、歌曲特征、行为类型枚举)=====

    /**
     * 用户行为事件类：记录用户实时交互行为
     * 生产环境应包含更多维度数据（如地理位置、设备信息等）
     */
    static class UserActionEvent {
        String userId;       // 用户唯一标识
        String songId;      // 歌曲标识
        ActionType action;  // (对目标歌曲进行的)行为类型枚举
        long timestamp;     // 事件发生时间

        public UserActionEvent(String userId, String songId, ActionType action) {
            this.userId = userId;
            this.songId = songId;
            this.action = action;
            this.timestamp = System.currentTimeMillis();
        }
    }

    /**
     * 用户画像类：动态记录用户特征（用户的id，最近播放列表，收藏集合，关键词搜索）
     * 使用并发集合保证线程安全
     */
    static class UserProfile {
        String userId;
        ConcurrentLinkedDeque<String> recentPlays = new ConcurrentLinkedDeque<>(); // 最近播放队列（维护最新10条）
        Set<String> favorites = ConcurrentHashMap.newKeySet();  // 收藏歌曲集合
        Map<String, Integer> searchKeywords = new ConcurrentHashMap<>(); // 搜索关键词及频次

        /**
         * 更新最近播放记录（自动淘汰旧记录）
         */
        public void updatePlayHistory(String songId) {
            if (recentPlays.size() >= 10)
            {
                recentPlays.removeFirst();
            }else{
                recentPlays.addLast(songId);
            }

        }
    }

    /**
     * 歌曲特征类：存储歌曲元数据（包含歌曲id，演唱者，流派，歌曲标签）
     * 实际应从数据库或特征服务加载
     */
    static class SongProfile {
        String songId;
        String artist;      // 演唱者
        String genre;       // 流派
        Set<String> tags;   // 歌曲标签（如"摇滚","怀旧"）

        public SongProfile(String songId, String artist, String genre) {
            this.songId = songId;
            this.artist = artist;
            this.genre = genre;
            this.tags = new HashSet<>(Arrays.asList(genre, artist));
        }
    }

    /**
     * 行为类型枚举
     */
    enum ActionType {
        PLAY,     // 播放行为（最直接影响推荐）
        FAVORITE, // 收藏行为（强偏好信号）
        SEARCH    // 搜索行为（显式需求表达）
    }

    // ================== 推荐引擎核心 ==================

    /**
     * 实时推荐引擎
     * 采用生产者-消费者模式处理事件
     */
    static class RecommendationEngine {
        // 内存数据存储（生产环境应使用Redis）
        private ConcurrentMap<String, UserProfile> userProfiles = new ConcurrentHashMap<>();//用户画像
        private ConcurrentMap<String, SongProfile> songMetadata = new ConcurrentHashMap<>();//歌曲元数据

        // 行为事件队列（生产环境应使用Kafka）
        private BlockingQueue<UserActionEvent> eventQueue = new LinkedBlockingQueue<>(1000);//队列的最大容量为 1000 个元素

        // 初始化加载歌曲元数据
        public void initializeSongs() {
            // 模拟加载歌曲数据
            songMetadata.put("song1", new SongProfile("song1", "周杰伦", "流行"));
            songMetadata.put("song2", new SongProfile("song2", "五月天", "摇滚"));
        }

        /**
         * 启动处理流程
         * 此方法创建一个新的线程，用于不断处理用户行为事件
         * 线程会一直运行，直到被显式中断
         */
        public void startProcessing() {
            // 创建一个单线程执行器，提交一个任务用于处理事件
            Executors.newSingleThreadExecutor().submit(() -> {
                // 循环等待并处理事件，直到线程被中断
                while (!Thread.currentThread().isInterrupted()) {
                    try {
                        // 1. 从队列获取事件（阻塞式）
                        UserActionEvent event = eventQueue.take();//take() 方法会从队列中获取并移除队列头部的元素

                        // 2. 更新用户画像
                        updateUserProfile(event);

                        // 3. 实时触发推荐（播放行为立即响应）
                        if (event.action == ActionType.PLAY) {
                            List<String> recommendations = generateRecommendations(event.userId);//生成推荐结果
                            deliverRecommendations(event.userId, recommendations);//打印推荐结果
                        }
                    } catch (InterruptedException e) {
                        // 捕获中断异常，设置当前线程为中断状态
                        Thread.currentThread().interrupt();
                    }
                }
            });
        }

        /**
         * 更新用户画像（核心方法）
         */
        private void updateUserProfile(UserActionEvent event) {
            // 从 userProfiles 中获取指定用户 ID 的用户档案，如果不存在（缺席absent）则创建一个新的 UserProfile 并存入 map
            UserProfile profile = userProfiles.computeIfAbsent(event.userId, k -> new UserProfile());

            // 根据用户行为类型更新用户档案
            switch (event.action) {
                case PLAY:
                    // 更新用户播放历史
                    profile.updatePlayHistory(event.songId);
                    break;
                case FAVORITE:
                    // 添加歌曲到用户收藏列表
                    profile.favorites.add(event.songId);
                    break;
                case SEARCH:
                    // 记录搜索关键词（这里简化为使用歌曲ID，实际应处理搜索词）
                    profile.searchKeywords.merge(event.songId, 1, Integer::sum);//Map<String, Integer>，其中键是搜索关键词（这里简化为歌曲 ID），值是该关键词被搜索的"次数"。
                    break;
            }
        }

        /**
         * 生成混合推荐结果
         */
        private List<String> generateRecommendations(String userId) {
            UserProfile user = userProfiles.get(userId);
            // 如果用户不存在，则返回默认推荐结果,处理冷启动问题
            if (user == null){
                return getDefaultRecommendations();
            }

            // 并行计算两种推荐结果
            List<String> userBased = userBasedRecommendation(user);//基于用户推荐
            List<String> itemBased = itemBasedRecommendation(user);//基于物品推荐

            // 混合策略：优先物品推荐，补充用户相似推荐
            Set<String> finalRecs = new LinkedHashSet<>();
            finalRecs.addAll(itemBased);
            finalRecs.addAll(userBased);

            // 去重并截取前10个结果
            return finalRecs.stream().distinct().limit(10).toList();
        }

        /**
         * 基于用户的协同过滤推荐
         * 策略：找到相似用户（根据收藏和搜索行为），推荐他们听的歌曲
         */
        private List<String> userBasedRecommendation(UserProfile user) {
            List<String> recommendations = new ArrayList<>();//推荐结果

            // 遍历所有用户找相似（生产环境应用聚类或索引优化）
            userProfiles.values().parallelStream()
                    .filter(other -> !other.userId.equals(user.userId)) // 排除自己，other为userProfiles的一个对象
                    .sorted(Comparator.comparingDouble(other ->
                            calculateUserSimilarity(user, other))) // 按相似度排序
                    .limit(3) // 取Top3相似用户
                    .forEach(similarUser -> {  //similarUser==>other
                        recommendations.addAll(similarUser.recentPlays);
                    });

            return recommendations;
        }

        /**
         * 计算用户相似度（简化的余弦相似度）
         */
        private double calculateUserSimilarity(UserProfile u1, UserProfile u2) {
            // 相似度计算维度：收藏交集 + 搜索关键词相似度
            Set<String> commonFavorites = new HashSet<>(u1.favorites);
            commonFavorites.retainAll(u2.favorites);

            double searchSimilarity = u1.searchKeywords.keySet().stream()
                    .filter(u2.searchKeywords::containsKey)
                    .count();

            return commonFavorites.size() * 0.7 + searchSimilarity * 0.3;
        }

        /**
         * 基于物品的特征推荐
         * 策略：根据用户最近播放歌曲的特征推荐相似歌曲
         */
        private List<String> itemBasedRecommendation(UserProfile user) {
            Set<String> recommendations = new HashSet<>();//使用set集合，去重

            // 分析最近播放特征
            user.recentPlays.forEach(songId -> {
                SongProfile song = songMetadata.get(songId);
                if (song == null) {
                    return;
                }

                // 策略1：推荐相同艺术家
                songMetadata.values().parallelStream() //对集合中的元素进行并行处理
                        .filter(s -> s.artist.equals(song.artist)) //s==>songMetadata下的song
                        .limit(2)//取Top2
                        .map(s -> s.songId)
                        .forEach(recommendations::add);

                // 策略2：推荐相同流派
                songMetadata.values().parallelStream() //对集合中的元素进行并行处理
                        .filter(s -> s.genre.equals(song.genre))
                        .limit(3)
                        .map(s -> s.songId)
                        .forEach(recommendations::add);
            });

            return new ArrayList<>(recommendations);
        }

        /**
         * 默认推荐（新用户/冷启动）
         */
        private List<String> getDefaultRecommendations() {
            return List.of("song1", "song2"); // 返回热门歌曲
        }

        /**
         * 推荐结果投递（模拟接口）
         */
        private void deliverRecommendations(String userId, List<String> songs) {
            System.out.printf("[实时推荐] 用户 %s 的推荐结果：%s\n", userId, songs);
        }

        /**
         * 接收用户行为事件
         */
        public void acceptEvent(UserActionEvent event) {
            eventQueue.offer(event);
        }
    }

    // ================== 示例运行 ==================
    public static void main(String[] args) throws InterruptedException {
        System.out.println("start。。。。。");
        RecommendationEngine engine = new RecommendationEngine();
        engine.initializeSongs();
        engine.startProcessing();

        // 模拟用户行为事件流
        engine.acceptEvent(new UserActionEvent("user1", "song1", ActionType.PLAY));
        engine.acceptEvent(new UserActionEvent("user1", "song2", ActionType.FAVORITE));
        engine.acceptEvent(new UserActionEvent("user1", "周杰伦", ActionType.SEARCH));

        System.out.println("模拟用户行为事件流："+engine.eventQueue);
        // 等待处理完成
        Thread.sleep(1000);
        System.out.println("end。。。。。");
    }
}